
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <mach/platform.h>
#include <linux/sched.h>
wait_queue_head_t FF_wqh;
char FF = -1;
volatile unsigned int ev_FF=0;
struct i2c_client *g_client=NULL;
struct mma8653_acc{
	short x;
	short y;
	short z;
};

struct i2c_device_id mma8653_id[]={
	{"mma8653",0},
	{	    },
};

irqreturn_t FF_isr(int irqnum,void *dev){
    int stat=-1;
    stat=gpio_get_value(PAD_GPIO_B+8);
    FF=stat;
    ev_FF=1;
    wake_up_interruptible(&FF_wqh);
    return IRQ_HANDLED;
}
void mma8653_hw_init(struct i2c_client * client){
	int ret=0;
	ret=i2c_smbus_read_byte_data( client, 0X0D);
	printk("<1>" "mma8653'addr is %#x,ID is %#x\n",client->addr,ret);
	i2c_smbus_write_byte_data(client, 0X2A, 0);
	i2c_smbus_write_byte_data(client, 0X0E, 0x10);
}

void config_active(void){
	unsigned char data=0;
	data=i2c_smbus_read_byte_data(g_client,0X2A);
	data|=1;
	i2c_smbus_write_byte_data(g_client,0X2A,data);
}

void config_FF(void){
	unsigned char data=0;
        // Freefall/Motion Interrupt Enable
	data=i2c_smbus_read_byte_data(g_client,0X2D);
	data|=(1<<2);
	i2c_smbus_write_byte_data(g_client,0X2D,data);
        //在自由落体过程中三轴加速度低于阈值即为一次MT_COUNT 计数
        //，超过0xb0次计数后,就会产生自由落体中断
	data=0xb0;
	i2c_smbus_write_byte_data(g_client,0X18,data);
        //检测加速度范围+-8g,阈值为0.063g
	data=0x81;
	i2c_smbus_write_byte_data(g_client,0X17,data);	
        //配置为自由落体功能,开启XYZ轴检测
	data=0xb8;
	i2c_smbus_write_byte_data(g_client,0X15,data);
}

/*推测中断是否为自由落体中断*/
void mma8653_readFF(int *isFF){
    if(i2c_smbus_read_byte_data(g_client,0x0C)&0x04){
        *isFF=1;
    }else{
        *isFF=0;
    }
}
/*获取三轴加速度二进制值*/
void mma8653_readacc(struct mma8653_acc *pdata){
    unsigned char acc[6];
    while(!(i2c_smbus_read_byte_data(g_client, 0x00)&0x08)){

    }
    i2c_smbus_read_i2c_block_data(g_client, 0x01, 7, acc);
    pdata->x=((acc[0]<<8)&0xff00)|acc[1];
    pdata->y=((acc[2]<<8)&0xff00)|acc[3];
    pdata->z=((acc[4]<<8)&0xff00)|acc[5];
    pdata->x=(pdata->x)>>6;
    pdata->y=(pdata->y)>>6;
    pdata->z=(pdata->z)>>6;
}

#define GET_XYZ 0X10001
#define GET_FF  0X10002
long mma8653_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){
	struct mma8653_acc pdata;
        int *isFF;
	int ret=0;
	switch(cmd){
		case GET_XYZ:
			config_active();
                        mma8653_readacc(&pdata);
			ret=copy_to_user((void *)arg,&pdata,sizeof(pdata));
		        break;
                
                case GET_FF:
                        config_FF();
                        wait_event_interruptible(FF_wqh,ev_FF);
                        mma8653_readFF(isFF);
                        if(isFF)
                        ret=copy_to_user((void *)arg,&FF,sizeof(FF));
                        break;
                default:
			return -EINVAL;
	}
	return 0;
}
/*
ssize_t FF_read(struct file *filp,char __user *buf,size_t len, loff_t *offset){
    int ret=0;
    wait_event_interruptible(hFF_wqh,ev_FF);

    ret=copy_to_user(buf,&FF,len);

    ev_FF=0;

    return len;
}*/
struct file_operations mma8653_fops = {
	.owner          = THIS_MODULE,
	.unlocked_ioctl = mma8653_ioctl,
	/*.read	     =FF_read,*/
};

struct miscdevice mma8653_misc = {
	.minor = MISC_DYNAMIC_MINOR,
	.name  = "mma865x",
	.fops  = &mma8653_fops,
};

int mma8653_probe(struct i2c_client *client, const struct i2c_device_id *id){
	g_client=client;
	config_FF();
 	int ret=request_irq(IRQ_GPIO_B_START+8,FF_isr,IRQF_TRIGGER_RISING, "FF",NULL);
	mma8653_hw_init(g_client);
	misc_register(&mma8653_misc);
	return 0;
}
int mma8653_remove(struct i2c_client *client){
	misc_deregister(&mma8653_misc);
	free_irq(IRQ_GPIO_B_START+8,NULL);
	return 0;
}

struct i2c_driver mma8653_drv = {
	.driver={
		.name= "mma865x",
	},
	.id_table=mma8653_id,
	.probe=mma8653_probe,
	.remove=mma8653_remove,
};

static int  __init mma8653_init(void){
	i2c_add_driver(&mma8653_drv);
}

static void __exit mma8653_exit(void){
        i2c_del_driver(&mma8653_drv);
}

MODULE_LICENSE("GPL");
module_init(mma8653_init);
module_exit(mma8653_exit);





